#region Copyright RenGuiYou. All rights reserved.

//=====================================================
// PandoraFrameWork
// Author:      RenGuiyou
// Feedback: 	mailto:750539605@qq.com
//=====================================================

#endregion

using System;
using System.Collections.Generic;
using UnityEngine;

namespace Pandora.Console
{
    [Serializable]
    public class LogWindow : IConsoleWindow
    {
        private LinkedList<LogNode> m_Logs = new LinkedList<LogNode>();
        private Vector2 m_LogScrollPosition = Vector2.zero;
        private int m_InfoCount;
        private int m_WarningCount;
        private int m_ErrorCount;
        private int m_FatalCount;
        private int m_rgyCount;

        private int m_MaxLine = 300;
        private bool m_InfoFilter = true;
        private bool m_WarningFilter = true;
        private bool m_ErrorFilter = true;
        private bool m_FatalFilter = true;

        private bool m_rgyFilter = true;

        private Color32 m_InfoColor = Color.white;
        private Color32 m_WarningColor = Color.yellow;
        private Color32 m_ErrorColor = Color.red;

        private Color32 m_FatalColor = new Color(0.7f, 0.2f, 0.2f);
        private int errorCount;

        public int WarningCount
        {
            get { return m_WarningCount; }
        }

        public int ErrorCount
        {
            get { return m_ErrorCount; }
        }

        public int FatalCount
        {
            get { return m_FatalCount; }
        }

        public void Initialize(params object[] args)
        {
            Application.logMessageReceived += OnLogMessageReceived;
        }

        public void Shutdown()
        {
            Application.logMessageReceived -= OnLogMessageReceived;
            Clear();
        }

        public void OnEnter()
        {
        }

        public void OnLeave()
        {
        }

        public void OnUpdate(float elapseSeconds, float realElapseSeconds)
        {
        }

        Vector2 oldInfoDrag;

        public void OnDraw()
        {
            var spaceHeight = PdrConsoleWindow.spaceHeight;
            var buttonStyle = PdrConsoleWindow.buttonStyle;
            var ButtonSize = PdrConsoleWindow.ButtonSize;
            var ToggleSize = PdrConsoleWindow.ToggleSize;
            getDownPos();
            RefreshCount();

            GUILayout.Space(spaceHeight);
            GUILayout.BeginHorizontal();
            {
                if (GUILayout.Button("Clear All", buttonStyle, GUILayout.Width(ButtonSize.x), GUILayout.Height(ButtonSize.y)))
                {
                    Clear();
                }

                GUILayout.FlexibleSpace();
                m_InfoFilter = GUILayout.Toggle(m_InfoFilter, string.Format("Log ({0})", m_InfoCount), buttonStyle, GUILayout.Width(ToggleSize.x), GUILayout.Height(ToggleSize.y));
                m_WarningFilter = GUILayout.Toggle(m_WarningFilter, string.Format("警告 ({0})", m_WarningCount), buttonStyle, GUILayout.Width(ToggleSize.x),
                    GUILayout.Height(ToggleSize.y));
                m_ErrorFilter = GUILayout.Toggle(m_ErrorFilter, string.Format("错误 ({0})", m_ErrorCount), buttonStyle, GUILayout.Width(ToggleSize.x),
                    GUILayout.Height(ToggleSize.y));
                m_FatalFilter = GUILayout.Toggle(m_FatalFilter, string.Format("异常 ({0})", m_FatalCount), buttonStyle, GUILayout.Width(ToggleSize.x),
                    GUILayout.Height(ToggleSize.y));
                if (m_WarningFilter || m_ErrorFilter || m_FatalFilter)
                {
                    m_rgyFilter = false;
                }
            }
            GUILayout.EndHorizontal();
            GUILayout.Space(spaceHeight / 2.0f);
            GUILayout.BeginHorizontal();
            {
                GUILayout.FlexibleSpace();
                m_rgyFilter = GUILayout.Toggle(m_rgyFilter, string.Format("我 ({0})", m_rgyCount), buttonStyle, GUILayout.Width(ToggleSize.x), GUILayout.Height(ToggleSize.y));
                if (m_rgyFilter)
                {
                    m_InfoFilter = true;
                    m_WarningFilter = false;
                    m_ErrorFilter = false;
                    m_FatalFilter = false;
                }
            }
            GUILayout.EndHorizontal();
            buttonStyle.alignment = TextAnchor.UpperLeft;
            GUILayout.Space(spaceHeight * 2);
            GUILayout.BeginVertical("box");
            {
                Vector2 drag = getDrag();
                if ((drag.x != 0) && (downPos != Vector2.zero))
                {
                    m_LogScrollPosition.x -= (drag.x - oldInfoDrag.x);
                }

                if ((drag.y != 0) && (downPos != Vector2.zero))
                {
                    m_LogScrollPosition.y += (drag.y - oldInfoDrag.y);
                }

                oldInfoDrag = drag;
                m_LogScrollPosition = GUILayout.BeginScrollView(m_LogScrollPosition);
                {
                    for (LinkedListNode<LogNode> i = m_Logs.First; i != null; i = i.Next)
                    {
                        LogType logType = i.Value.LogType;
                        if ((logType == LogType.Warning && !m_WarningFilter) ||
                            (logType == LogType.Error && !m_ErrorFilter) ||
                            (logType == LogType.Exception && !m_FatalFilter))
                        {
                            continue;
                        }

                        if (logType == LogType.Log)
                        {
                            LogBelong logBelong = i.Value.LogBelong;

                            if (m_rgyFilter)
                            {
                                if (logBelong == LogBelong.None)
                                {
                                    continue;
                                }

                                if ((logBelong == LogBelong.RGY && !m_rgyFilter))
                                {
                                    continue;
                                }
                            }
                            else if (!m_InfoFilter)
                            {
                                continue;
                            }
                        }

                        GUILayout.BeginVertical("box");
                        {
                            GUILayout.Label(GetLogString(i.Value));
                        }
                        GUILayout.EndVertical();
                        GUILayout.Space(spaceHeight);
                    }
                }
                GUILayout.EndScrollView();
            }
            GUILayout.EndVertical();
            buttonStyle.alignment = TextAnchor.MiddleCenter;
        }

        private void Clear()
        {
            m_Logs.Clear();
        }

        public void RefreshCount()
        {
            m_InfoCount = 0;
            m_WarningCount = 0;
            m_ErrorCount = 0;
            m_FatalCount = 0;
            m_rgyCount = 0;
            for (LinkedListNode<LogNode> i = m_Logs.First; i != null; i = i.Next)
            {
                switch (i.Value.LogType)
                {
                    case LogType.Log:
                        m_InfoCount++;
                        break;
                    case LogType.Warning:
                        m_WarningCount++;
                        break;
                    case LogType.Error:
                        m_ErrorCount++;
                        break;
                    case LogType.Exception:
                        m_FatalCount++;
                        break;
                }

                switch (i.Value.LogBelong)
                {
                    case LogBelong.RGY:
                        m_rgyCount++;
                        break;
                }
            }
        }

        private void OnLogMessageReceived(string logMessage, string stackTrace, LogType logType)
        {
            if (errorCount > 10)
            {
                return;
            }

            if (logType == LogType.Assert)
            {
                logType = LogType.Error;
            }

            if (logType == LogType.Error || logType == LogType.Exception)
            {
                errorCount++;
            }

            m_Logs.AddLast(new LogNode(logType, logMessage, stackTrace));
            while (m_Logs.Count > m_MaxLine)
            {
                m_Logs.RemoveFirst();
            }
        }

        private string GetLogString(LogNode logNode)
        {
            Color32 color = GetLogStringColor(logNode.LogType);
            return string.Format("<color=#{0}{1}{2}{3}>{4}</color>",
                color.r.ToString("x2"), color.g.ToString("x2"), color.b.ToString("x2"), color.a.ToString("x2"),
                logNode.LogMessage);
        }

        internal Color32 GetLogStringColor(LogType logType)
        {
            Color32 color = Color.white;
            switch (logType)
            {
                case LogType.Log:
                    color = m_InfoColor;
                    break;
                case LogType.Warning:
                    color = m_WarningColor;
                    break;
                case LogType.Error:
                    color = m_ErrorColor;
                    break;
                case LogType.Exception:
                    color = m_FatalColor;
                    break;
            }

            return color;
        }

        Vector2 downPos;

        void getDownPos()
        {
            if (Application.platform == RuntimePlatform.Android || Application.platform == RuntimePlatform.IPhonePlayer)
            {
                if (Input.touches.Length == 1 && Input.touches[0].phase == TouchPhase.Began)
                {
                    downPos = Input.touches[0].position;
                }
            }
            else
            {
                if (Input.GetMouseButtonDown(0))
                {
                    downPos.x = Input.mousePosition.x;
                    downPos.y = Input.mousePosition.y;
                }
            }
        }

        Vector2 mousePosition;

        Vector2 getDrag()
        {
            if (Application.platform == RuntimePlatform.Android || Application.platform == RuntimePlatform.IPhonePlayer)
            {
                if (Input.touches.Length != 1)
                {
                    return Vector2.zero;
                }

                return Input.touches[0].position - downPos;
            }

            if (Input.GetMouseButton(0))
            {
                mousePosition = Input.mousePosition;
                return mousePosition - downPos;
            }

            return Vector2.zero;
        }
    }

    public enum LogBelong
    {
        None,
        RGY
    }

    public class LogNode
    {
        private readonly LogBelong m_LogBelong;
        private readonly LogType m_LogType;
        private readonly string m_LogMessage;
        private readonly string m_StackTrack;
        private string m_rgyColor = "white";

        public LogNode(LogType logType, string logMessage, string stackTrack)
        {
            m_LogType = logType;
            m_LogMessage = logMessage;
            m_StackTrack = stackTrack;
            m_LogBelong = LogBelong.None;
            if (logMessage.ToLower().IndexOf(m_rgyColor, StringComparison.Ordinal) != -1)
            {
                m_LogBelong = LogBelong.RGY;
            }
        }

        public LogType LogType
        {
            get { return m_LogType; }
        }

        public LogBelong LogBelong
        {
            get { return m_LogBelong; }
        }

        public string LogMessage
        {
            get { return m_LogMessage; }
        }

        public string StackTrack
        {
            get { return m_StackTrack; }
        }
    }
}